//for QSqlite
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
#include <QtCore/QVariant>
#include <QDebug>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <sys/select.h>

#include <QTimer>
#include <QFile>
#include <QDataStream>
#include <QString>
#include <QMessageBox>

#include "boardregthread.h"

boardRegThread::boardRegThread(QObject *parent) :
    QThread(parent)
{
    timer1 = new QTimer(this);
    timer1->setInterval(200000);//200s
    timer1->setSingleShot(true);
    isTimeOut = false;
    connect(timer1,SIGNAL(timeout()),this,SLOT(timeOutSlot()));

}

void boardRegThread::run()
{
    int j = 0, k = 0;
    int num,nread;
    fd_set rfds;
    struct timeval tv;

    i = 1;//boardnum ,use as global variable
    boardRegSum = 0;
    memset(&boardRegFlag,0,sizeof(boardRegFlag));

    can_fd = open("/dev/can", O_RDWR);
    if(can_fd<=0)
    {
        qDebug()<<"in boardreg thread: can't open  can_dev "<<endl;
        emit regFault();
        return;
    }
    else
        qDebug() <<"open  can_dev successfully!"<<endl;

    emit regStart();
    qDebug() << "board registing start..." << endl;
    sendBroadcast();
    qDebug()<<"3s 倒计时中......bingbingbing...";
    sleep(3);//100s

    /****   single board request registration information command frame */
    for( i = 1; i <= 2 ; i++ )//i = 1; i <= BOARDMAXINUM ; i++
    {
        /*****  send single board  register command frame to board i *******/
        memset (&nodeType, 0x0, sizeof(nodeType));
        memset (&loopRegNum, 0x0, sizeof(loopRegNum));
        memset (&louxianReg,0,sizeof(louxianReg));
        memset (&zhikongReg,0,sizeof(zhikongReg));
        memset (&zongxianReg,0,sizeof(zongxianReg));
        memset (&sendFrame, 0x0, sizeof(struct can_frame));
        memset (&recvFrame, 0x0, sizeof(struct can_frame));

        QString fileName = QString("./Board/Board%01.BIN").arg(i);
        QFile boardFile(fileName);
        if (!boardFile.open(QIODevice::ReadWrite ))
        {
            qDebug() << "cant open file for writing";
            this->exit();
            return;
        }
        QDataStream infile(&boardFile);

        sendSingle();//send single frame to board

        FD_ZERO(&rfds);//清空串口接收端口集
        FD_SET(can_fd, &rfds);//设置串口接收端口集
        tv.tv_sec = 3;
        tv.tv_usec = 0;
        num = select (can_fd + 1, &rfds, NULL, NULL, &tv);
        if (num == 0)//no ack in 3 seconds
        {
            /*do something here ,then continue to  next board*/
            boardState = OFFLINE;
            infile << boardState;
            boardFile.close();
            qDebug() << "no receive in 3 seconds,board "<< i <<" is offline"<<endl;
            continue;
        }
        else
        {
            do
            {
                if(isTimeOut)
                {
                    /**** board fault, no register over frame received in 200s ******/
                    boardState = FAULT;
                    infile << boardState;
                    boardFile.close();
                    qDebug() <<"no registerOver frame received in 200 seconds,board fault"<<endl;
                    break;
                }
                /***  deal with the info reported of board i  ***/
                FD_ZERO(&rfds);//清空串口接收端口集
                FD_SET(can_fd, &rfds);//设置串口接收端口集
                tv.tv_sec = 3;
                tv.tv_usec = 0;
                num = select (can_fd + 1, &rfds, NULL, NULL, &tv);
                if (num == 0)
                {
                    boardState = OTHER;
                    infile << boardState;
                    boardFile.close();
                    qDebug() << "no receive in 3 seconds"<<endl;
                    break;
                }
                nread = read(can_fd, &recvFrame, sizeof(struct can_frame));
                //qDebug()<<"nread is "<<nread;

                if(recvFrame.ti == TI_Ack && recvFrame.priority == Priority4 )
                {
                    if(recvFrame.src_addr == sendFrame.dest_addr )
                    {
                        timer1->start();
                        qDebug() <<"receive ack from board "<<recvFrame.src_addr;
                    }
                    else
                        break;
                }
                else if(recvFrame.ti == TI_RegisterInfReport && recvFrame.priority == Priority5 )
                {
                    dealInfo(recvFrame.src_addr);
                    qDebug() <<"receive info from board "<<recvFrame.src_addr;
                }
                else if(recvFrame.ti == TI_RegisterOver && recvFrame.priority == Priority5 )
                {    /*  END of board i register, save info to file for test */
                    /*  header of 8 bytes
                        @int boardstate; //4 byte
                        @unsigned char loopRegNum[4];//4 byte
                    */
                    qDebug() <<"receive overFrame from board "<<recvFrame.src_addr;
                    timer1->stop();
                    sendAck();
                    if(i < LOUXIANNUM)
                    {
                        for( j = 0; j<LOOPMAXINUM; j++)
                        {
                            for( k = 0; k<NODEMAXINUM; k++)
                                if (nodeType[j][k] != 0xFF && nodeType[j][k] != 0x00)
                                    loopRegNum[j]+=1;
                        }
                        saveNodeToDB(i, nodeType);
                        infile <<  boardState;
                        infile.writeRawData((char*)loopRegNum,sizeof(loopRegNum));
                        infile.writeRawData((char*)nodeType[0],sizeof(nodeType));
                    }
                    else if( i == LOUXIANNUM)
                    {
                        louxianRegNum = 0;
                        for(j = 0; j< LOUXIANMAXNUM; j++)
                            if(louxianReg[j] == LOUXIAN)
                                louxianRegNum += 1;
                        boardState = ONLINE;
                        infile << boardState ;
                        infile << louxianRegNum;
                        infile.writeRawData((char*)louxianReg,sizeof(louxianReg));

                    }
                    else if( i == ZHIKONGNUM)
                    {
                        zhikongRegNum = 0;
                        for(j = 0; j< ZHIKONGMAXNUM; j++)
                            if(zhikongReg[j] == ZHIKONG)
                                zhikongRegNum += 1;
                        boardState = ONLINE;
                        infile << boardState ;
                        infile << zhikongRegNum;
                        infile.writeRawData((char*)zhikongReg,sizeof(zhikongReg));
                    }
                    else if( i == ZONGXIANNUM)
                    {
                        zongxianRegNum = 0;
                        for(j = 0; j< ZHIKONGMAXNUM; j++)
                            if(zongxianReg[j] == ZHIKONG)
                                zongxianRegNum += 1;
                        boardState = ONLINE;
                        infile << boardState << zongxianRegNum;
                        infile.writeRawData((char*)zongxianReg,sizeof(zongxianReg));
                    }
                    boardFile.close();
                    boardRegFlag[i-1] = TRUE;
                    boardRegSum += 1;
                }
            }while(recvFrame.ti != TI_RegisterOver );//&& recvFrame.priority == Priority5 );//while((recvFrame.pri_ti & 0x1F) != TI_RegisterOver);
        }
    }
    close(can_fd);
    //saveBoardRegFlagToDB(boardRegFlag);
    emit(regAllOver(boardRegSum));
    close(can_fd);

    qDebug() <<"total registerd board number is :"<< boardRegSum;

}

void boardRegThread::sendBroadcast()
{
    /******  send broadcast frame ************/
    memset (&sendFrame, 0x0, sizeof(struct can_frame));
    sendFrame.priority = Priority2;
    sendFrame.ti = TI_BroadcastRegister;
    sendFrame.src_addr = CONTROLLADDR;
    sendFrame.dest_addr = 0x00;
    sendFrame.can_dlc = 0x00;
    qDebug() <<"send broadcast frame ...";
    nwrite = write(can_fd, &sendFrame, sizeof(struct can_frame));
//    qDebug() <<"nwrite is "<<nwrite;
    qDebug() <<"send broadcast frame over...";

}
void boardRegThread::sendAck()
{
    /* send ACK  ****/
    memset (&sendFrame, 0x0, sizeof(struct can_frame));
    sendFrame.priority = Priority4;
    sendFrame.ti = TI_Ack;
    sendFrame.src_addr = CONTROLLADDR;
    sendFrame.dest_addr = recvFrame.src_addr;
    sendFrame.can_dlc = 0x00;
    qDebug() <<" END of Register of current board...send ack to board "<<recvFrame.src_addr;
    nwrite = write(can_fd, &sendFrame, sizeof(struct can_frame));
    //qDebug() <<"nwrite is "<<nwrite;
    qDebug() <<" send ack to board "<<recvFrame.src_addr<<" over...";
}
void boardRegThread::sendSingle()
{
    sendFrame.priority = Priority5;
    sendFrame.ti = TI_SignalBoardReqRegisterInf;
    sendFrame.src_addr = CONTROLLADDR;
    sendFrame.dest_addr = i;
    sendFrame.can_dlc = 0x00;
    qDebug() <<"send single frame to board "<<i;
    nwrite = write(can_fd, &sendFrame, sizeof(struct can_frame));
    //qDebug() <<"nwrite is "<<nwrite;
    qDebug() <<"send single frame over...";

}
void boardRegThread::timeOutSlot()
{
    isTimeOut = true;
}

void boardRegThread::saveFlagFile()
{
    QString fileName = QString("./Board/BoardRegFlag.BIN");
    QFile boardFile(fileName);
    if (!boardFile.open(QIODevice::ReadWrite ))
    {
        qDebug() << "cant open file for writing";
        this->exit();
        return;
    }

    QDataStream infile(&boardFile);
    infile.writeRawData((char*)boardRegFlag,sizeof(boardRegFlag));
    boardFile.close();

}

void  boardRegThread::dealInfo(int i)
{
    int loopid = 0, nodeid = 0;
    if(i < LOUXIANNUM)// board 1~32
    {
        /* current board num is i, loop num is recvFrame.data[0]*/
        loopid = recvFrame.data[0];
        nodeid = recvFrame.data[1];
        memcpy( &nodeType[loopid-1][nodeid-1], &recvFrame.data[2], 5*sizeof(unsigned char));
    }
    else if( i == LOUXIANNUM)//louxian board 33
    {
        nodeid = recvFrame.data[0];
        memcpy( &louxianReg[nodeid-1], &recvFrame.data[1], 6*sizeof(unsigned char));
    }
    else if( i == ZHIKONGNUM)//zhikong board 34
    {
        nodeid = recvFrame.data[0];
        memcpy( &zhikongReg[nodeid-1], &recvFrame.data[1], 6*sizeof(unsigned char));
    }
    else if( i == ZONGXIANNUM)//zongxian board 35
    {
        nodeid = recvFrame.data[0];
        memcpy( &zongxianReg[nodeid-1], &recvFrame.data[1], 6*sizeof(unsigned char));
    }
}
void boardRegThread::saveNodeToDB(int boardnum, unsigned char nodeType[][256])
{

    int host = 1;
    #if 0
    //int iobr = 0;
    //int loop = 0;
    //int node = 0;
    int tmp = 0;
    QSqlDatabase::database().transaction();
    QSqlQuery query;
    query.setForwardOnly(true);
    /*update type of unitset table */
    query.exec(QString("select type from unitset WHERE unitid >= %1 AND unitid <= %2").arg((host << 24) | (boardnum << 16) | (0 << 8) | 0)
                                                                                .arg((host << 24) | (boardnum << 16) | 0XFFFF));
    for (int loop = 1;loop <= 4;loop++)
    {
        for (int node = 1;node <= 255;node++)
        {
            if (query.next())
            {
                tmp = query.value(0).toInt();
            }
            QSqlQuery query1;
            query1.clear();
            query1.prepare(QString("UPDATE unitset SET "
                                    "regist=?, type=?"
                                    "WHERE unitid = %1").arg((host<<24) | (boardnum <<16) | (loop <<8) | node));
            if ((int)nodeType[loop - 1][node - 1] == 0xFF)
            {
                query1.addBindValue(false);
            }
            else
            {
                query1.addBindValue(true);
            }
            query1.addBindValue((tmp & 0xFFFF00FF) | ((int)nodeType[loop - 1][node - 1] << 8));
            bool bSuccess = query1.exec();
            if (!bSuccess)
            {
                QSqlError lastError = query1.lastError();
                qDebug() << lastError.driverText()
                            << QString(QObject::tr("saveToDB update error"));
            }
        }
    }
    QSqlDatabase::database().commit();
    #endif
    QSqlDatabase::database().transaction();
    QSqlQuery query1;
    bool bSuccess = query1.exec(QString("DELETE FROM unitset WHERE unitid >= %1 AND unitid <= %2")
                                .arg((host << 24) | (boardnum << 16) | (0 << 8) | 0).arg((host << 24) | (boardnum << 16) | 0XFFFF));

    if (!bSuccess)
    {
        QSqlError lastError = query1.lastError();
        qDebug() << lastError.driverText()
                     <<QString(QObject::tr("delete iobrinfo error"));
    }
    QSqlDatabase::database().commit();

    //ADD
    /*Before the opening of each transaction，adding this code to improve the speed*/
    QSqlDatabase::database().transaction();
    QSqlQuery query;
    query.prepare("INSERT INTO unitset(id, regist, shield, restated, level, unitid, geo_addr, type, delay, str)"
                                    "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
    for (int loop = 1;loop <= 4;loop++)
    {
        for (int node = 1;node <= 255;node++)
        {
            //if((iobr-1)*4*255+(loop-1)*255+node != rowCount++)
                //qDebug() << QObject::tr("false");
            query.addBindValue((boardnum-1)*4*255+(loop-1)*255+node);
            if ((int)nodeType[loop - 1][node - 1] == 0xFF)
            {
                query.addBindValue(false);
            }
            else
            {
                query.addBindValue(true);
            }
            query.addBindValue(false);
            query.addBindValue(false);
            query.addBindValue(false);
            query.addBindValue((host<<24) | (boardnum<<16) | (loop<<8) | node);
            query.addBindValue(0x00000000);
            query.addBindValue(0x00FF0000 | ((int)nodeType[loop - 1][node - 1] << 8));
            query.addBindValue(0x00000000);
            query.addBindValue(QString("%1").arg(node));
            bool bSuccess = query.exec();
            if (!bSuccess)
            {
                QSqlError lastError = query.lastError();
                qDebug() << lastError.driverText()
                            <<QString(QObject::tr("insert unitset error"));
            }
        }
    }
    /*Close the transaction*/
    QSqlDatabase::database().commit();
}

void boardRegThread::saveBoardRegFlagToDB(bool boardRegFlag[BOARDMAXINUM])
{
    QSqlDatabase::database().transaction();
    QSqlQuery query;
    query.setForwardOnly(true);
    for (int iobr = 1;iobr <= 32;iobr++)
    {
        query.clear();
        query.prepare(QString("UPDATE iobrinfo SET "
                                "iobrregist=?"
                              "WHERE id = %1").arg(iobr));
        query.addBindValue(boardRegFlag[iobr - 1]);
        bool bSuccess = query.exec();
        if (!bSuccess)
        {
            QSqlError lastError = query.lastError();
            qDebug() << lastError.driverText()
                        << QString(QObject::tr("saveBoardRegFlagToDB update error"));
        }
    }
    QSqlDatabase::database().commit();
}
boardRegThread::~boardRegThread()
{
        delete(this);
}
